#if !defined(PERCEPTION_VISUALIZER_DEBUG_H_)
#define PERCEPTION_VISUALIZER_DEBUG_H_

#include <pcl/common/common_headers.h>
#include <pcl/visualization/pcl_visualizer.h>

#include <mutex>
#include <thread>

#include "common/map_info.h"

namespace autodrive {
namespace perception {
class PerceptionVisualizerDebug final {
 public:
  PerceptionVisualizerDebug(PerceptionVisualizerDebug &&) = delete;
  PerceptionVisualizerDebug(const PerceptionVisualizerDebug &) = delete;
  PerceptionVisualizerDebug &operator=(PerceptionVisualizerDebug &&) = delete;
  PerceptionVisualizerDebug &operator=(const PerceptionVisualizerDebug &) =
      delete;
  ~PerceptionVisualizerDebug();

 protected:
  PerceptionVisualizerDebug();

  /// @brief mian task function
  /// @param processorPtr BevFusionProcessor instance
  static void vis_task(PerceptionVisualizerDebug *const) noexcept;

 public:
  ///@brief Get instance
  ///@return PerceptionVisualizer instance
  static PerceptionVisualizerDebug &GetInstance() noexcept;

  /// @brief init
  void Init() noexcept(false);

  /// @brief
  /// @return
  bool IsInit() noexcept { return is_init_; }

  /// @brief
  /// @return
  bool IsDispose() noexcept { return is_dispose_; }

  void UpdateMatch(MapElementPointSet const &predictPoints,
                   MapElementPointSet const &currentPoints,
                   std::string const &matchInfo) noexcept;

  /// @brief
  /// @param resultData
  /// @param gtData
  template <typename T>
  void Update(std::int64_t const &timestamp,
              MapElementSet<T> const &histMapElement,
              MapElementSet<T> const &predMapElement,
              MapElementSet<T> const &currentMapElement) noexcept {
    std::lock_guard<std::mutex> lock(update_vis_lock_);
    cloud_ptr_->clear();
    for (auto const &ele : histMapElement) {
      for (auto const &ep : ele.points) {
        pcl::PointXYZRGB p;
        p.x = ep.x;
        p.y = ep.y;
        p.z = ep.z;
        p.r = 255;
        p.g = 0;
        p.b = 0;
        cloud_ptr_->emplace_back(p);
      }
    }

    for (auto const &ele : predMapElement) {
      for (auto const &ep : ele.points) {
        pcl::PointXYZRGB p;
        p.x = ep.x;
        p.y = ep.y;
        p.z = ep.z;
        p.r = 0;
        p.g = 255;
        p.b = 0;
        cloud_ptr_->emplace_back(p);
      }
    }

    for (auto const &ele : currentMapElement) {
      for (auto const &ep : ele.points) {
        pcl::PointXYZRGB p;
        p.x = ep.x;
        p.y = ep.y;
        p.z = ep.z;
        p.r = 255;
        p.g = 255;
        p.b = 255;
        cloud_ptr_->emplace_back(p);
      }
    }

    timestamp_ = timestamp;
    is_update_vis_ = true;
  }

 protected:
  /// @brief sleep for millisecond
  /// @param milliseconds
  inline void msleep(int64_t milliseconds) noexcept {
    std::chrono::milliseconds dura{milliseconds};
    std::this_thread::sleep_for(dura);
  }

 private:
  /// @brief  init call once flag
  mutable std::once_flag init_flag_;

  /// @brief init flag
  bool is_init_;

  /// @brief main task
  std::thread vis_thread_;

  /// @brief
  bool is_dispose_;

  bool is_update_vis_;

  bool is_continue_;
  /// @brief
  std::mutex update_vis_lock_;

  /// @brief
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_ptr_;

  /// @brief
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr match_cloud_ptr_;

  std::string match_info_;

  /// @brief timestamp
  std::int64_t timestamp_;
};

}  // namespace perception
}  // namespace autodrive

#endif  // MACRO
